package aps.module_Machines;

//Java
import java.util.ArrayList;
import java.util.List;

import net.minecraft.src.NBTTagCompound;
import net.minecraft.src.TileEntity;
import net.minecraftforge.common.ForgeDirection;
import aps.module_Core.GuiIDs;
import aps.module_Core.TileEntityAPSPowered;
import aps.module_Core.module_Core.APSBlockTypes;
import buildcraft.api.core.Position;
import buildcraft.api.gates.IAction;
import buildcraft.api.gates.IActionReceptor;
import buildcraft.api.power.IPowerReceptor;
import buildcraft.core.network.PacketPayload;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.TilePacketWrapper;
//Vanilla Minecraft
//APS
//Buildcraft

public class TileEntityEnergyDirector extends TileEntityAPSPowered implements IPowerReceptor, IActionReceptor
{
	static int MaxPowerIn = 750;
	static int MaxPowerOut = 750;
	
	int lastSideViewed = 0;
	
	float PowerLevel;
	float PowerDrain;
	@TileNetworkData float[][] PowerData; 
	boolean[] faceSwitch; //enable/disable faces
	//Power Priority (1-4), Minimum Power (0-MaxPowerIn), Power Gate (0.0-1.0), Power Allotted, Minimum Power Allotted (for GUI), Gate Power Allotted (for GUI)
	//fraction of power out through here (0.0 is no power, 1.0 is full power.
	//e.g. 1 with 1.0, 2 with 0.5 and 1 with 0.0. The 1.0 will get half the total power in and each 0.5 will get a quarter of the power in
	
	String[] text = new String[3];  //Client side text display
	
	boolean routeMode = false; // a new mode for the energy director. In this mode the energy director will use Priority first.
	int defaultRoute = -1;
	/*
	 * Using priority first, Sides with lower number get supplied with power first.
	
	*/
	//used for communication
	public static TilePacketWrapper updateMessageWrapper = new TilePacketWrapper(UpdateMessage.class);
	
	public TileEntityEnergyDirector()
	{
		super(0, 48, 1, 1, 0, 1, MaxPowerIn, 1, 4000, APSBlockTypes.Machine);
		hasGUI = true;
		GuiID = GuiIDs.GUI_ENERGYDIRECTOR;
		setTexture(2, 49); setTexture(3, 51);
		setTexture(4, 52); setTexture(5, 50);
		PowerLevel = 0;
		PowerData = new float[6][4];
		faceSwitch = new boolean[4];
		for(int i = 0; i < 4; i++)
		{
			PowerData[0][i] = 0.0f;
			PowerData[1][i] = 0.0f;
			PowerData[2][i] = 0.0f;
			PowerData[3][i] = 0.0f;
			PowerData[4][i] = 0.0f;
			PowerData[5][i] = 0.0f;
			faceSwitch[i] = true;
		}
		setText("[b]Startup: Complete");
	}
	
	@Override
	public void updateEntity()
	{			
		if (worldObj.isRemote) return;

		//powerProvider.update((IPowerReceptor) this);
		PowerLevel = powerProvider.useEnergy(1, 4000, true); //Get total power in
		
		if (routeMode == false)
			splitPower();
		else 
			routePower();
			
		
		//Send sync packets to client
		sendNetworkUpdate();
	}
	
	public void splitPower() //split power equally between all sides according to user settings
	{	
		
	}
	
	public void routePower() //split power according to favor set by priority on each side.
	{
		if (defaultRoute == -1) return;
		
		if (worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)){ //on redstone signal, all power goes down default route
			sendOutPower(defaultRoute, PowerLevel);
			return;
		}else{
			for (int priority = 0; priority < 3; priority++)
			{
				if (PowerLevel == 0) return;//out of power
				
				for (Integer side : getConnectedSides()){
					if (side != defaultRoute && getPriority(side) == priority && faceSwitch[side] == true)
						sendOutPower(side, PowerData[1][side]);
				}
			}
			
			// send rest through default route
			if (PowerLevel > 0){
				sendOutPower(defaultRoute, PowerLevel);
			}
		}
	}
	
	//this function gets used alot
	public void sendOutPower(int direction, float amount)
	{
		PowerLevel = PowerLevel - amount;
		
		Position pos = new Position(xCoord,yCoord, zCoord, ForgeDirection.VALID_DIRECTIONS[translateToForgeDirection(direction)]);
		pos.moveForwards(1);
		
		TileEntity tile = worldObj.getBlockTileEntity((int)pos.x, (int)pos.y, (int)pos.z);
		
		if (isPoweredTile(tile)){			
			IPowerReceptor receptor = (IPowerReceptor)tile;
			receptor.getPowerProvider().receiveEnergy(amount, pos.orientation.getOpposite());
		}
	}
	
	public void setMinimumPowerValue(int Side, float MinimumPower)
	{
		if(Side < 0 || Side > 3) return;
		if(MinimumPower < 0 || MinimumPower > MaxPowerIn) return;
		PowerData[1][Side] = MinimumPower;
	}
	
	public void setGateValue(int Side, float GateValue)
	{
		if(Side < 0 || Side > 3) return;
		if(GateValue < 0 || GateValue > 1) return;
		PowerData[2][Side] = GateValue;
	}
	
	public void setPriority(int Side, int Priority)
	{
		if(Side < 0 || Side > 3 || !routeMode) return;
		if(Priority < 0 || Priority > 3) return;
		
		if (Priority == 3){
			if (defaultRoute == -1){
				defaultRoute = Side;
			}
		} else {
			if (defaultRoute == Side) //Removing defalt route from this side
				defaultRoute = -1;
		}
		
		PowerData[0][Side] = Priority;
	}
	
	public void setMode(boolean mode)
	{
		if (mode != routeMode){
			routeMode = mode;
			if (mode == true){
				setText("Routing mode");
				if (defaultRoute == -1)
				{
					setText("[r]No default route");
					setText("[r]Redirector disabled");
				}
			} else {
				setText("Split mode");
				setText("[r]Not implemented");
			}
		}
	}
	
	public void setText(String string)
	{
		for (int i = 0; i < 2; i++){
			if (text[i] == null || text[i].equals("")){
				text[i] = string;
				return;
			}
		}
			
		//we have exceeded the 3 line limit, replace element 1 with element 2, and element 2 with 3. Then make element 3 the new string
		
		for (int i = 0; i < 2; i++)  //copy over the first 2 elements
		{
			text[i] = text[i + 1];
		}
		text[2] = string;
	}
	
	public float getScaledMinimumPowerValue(int Side, int MaxLevel)
	{
		if(Side < 0 || Side > 3) return 0;
		return (PowerData[1][Side] / MaxPowerIn) * MaxLevel;
	}
	
	public float getScaledGateValue(int Side, int MaxLevel)
	{
		if(Side < 0 || Side > 3) return 0;
		return PowerData[2][Side] * MaxLevel;
	}
	
	public float getScaledLastPowerOutput(int Side, int MaxLevel)
	{
		if(Side < 0 || Side > 3) return 0;
		return (PowerData[3][Side] / MaxPowerOut) * MaxLevel;
	}
	
	public float getScaledLastMinimumPowerOut(int Side, int MaxLevel)
	{
		if(Side < 0 || Side > 3) return 0;
		return (PowerData[4][Side] / MaxPowerOut) * MaxLevel;
	}
	
	public float getScaledLastGatePowerOut(int Side, int MaxLevel)
	{
		if(Side < 0 || Side > 3) return 0;
		return (PowerData[5][Side] / MaxPowerOut) * MaxLevel;
	}
	
	public int getPriority(int Side)
	{
		if(Side < 0 || Side > 3) return 0;
		return (int) PowerData[0][Side];
	}
	
	public int translateToForgeDirection(int blockSide)
	{
		switch(blockSide){
			case 0:
				return 2;
			case 1:
				return 5;
			case 2:
				return 3;
			case 3:
				return 4;
		}
		
		return -1; //should never happen
	}
	
	public List<Integer> getConnectedSides()
	{
		List<Integer> result = new ArrayList<Integer>();
		
		if (isPoweredTile(worldObj.getBlockTileEntity(xCoord + 1, yCoord, zCoord))) result.add(1); //east
		if (isPoweredTile(worldObj.getBlockTileEntity(xCoord - 1, yCoord, zCoord))) result.add(3); //west
		if (isPoweredTile(worldObj.getBlockTileEntity(xCoord, yCoord, zCoord + 1))) result.add(2); //south
		if (isPoweredTile(worldObj.getBlockTileEntity(xCoord, yCoord, zCoord - 1))) result.add(0); //north
		
		return result;
	}
	
	public int getTotalOutput()
	{
		int total = 0;
		for (int i = 0; i < 4; i++)
		{
			if (i == defaultRoute) break; //if route side is default, miss.
			total += PowerData[1][i];
		}
		return total;
	}
	
	public int powerRequest()
	{
		if (getConnectedSides().size() == 0)
			return 0; //nothing connected
		
		if (getTotalOutput() == 0)
			return 0;
		
		if (routeMode){ //is route mode on but no default route side set
			if (defaultRoute == -1 && worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord))
				return 0;
			else if (!getConnectedSides().contains(defaultRoute)) //default route not connected
				return 0;
		}else{
			//if (getTotalOutput(2) == 0)
				return 0;
		}

		return MaxPowerIn;
	}
	
	public void readFromNBT(NBTTagCompound nbttagcompound)
    {
    	super.readFromNBT(nbttagcompound);
		
    	for(int i = 0; i < 3; i++)
    	{
    		for (int j = 0; j < 4; j++)
    		{
    			PowerData[i][j] = nbttagcompound.getFloat("powerdata" + i + j);
    		}
    	}
    	this.routeMode = nbttagcompound.getBoolean("routemode");
    	this.defaultRoute = nbttagcompound.getInteger("defaultroute");
    }
	
    public void writeToNBT(NBTTagCompound nbttagcompound) {
    	super.writeToNBT(nbttagcompound);
    	
    	for(int i = 0; i < 3; i++)
    	{
    		for (int j = 0; j < 4; j++)
    		{
    			nbttagcompound.setFloat("powerdata" + i + j, PowerData[i][j]);
    		}
    	}
    	nbttagcompound.setBoolean("routemode", this.routeMode);
    	nbttagcompound.setInteger("defaultroute", this.defaultRoute);
    }
    
	@Override
	public void kill() {}

	/*@Override
	public GuiScreen getGUI(EntityPlayer entityplayer) {
		return new GuiEnergyDirector(this, entityplayer.inventory, lastSideViewed);
	}*/
	
	/*@Override
	public LinkedList<ITrigger> getPipeTriggers(IPipe pipe) {
		return null;
	}

	@Override
	public LinkedList<ITrigger> getNeighborTriggers(Block block, TileEntity tile)
	{
		LinkedList<ITrigger> triggers = new LinkedList<ITrigger>();
		
		if(tile instanceof TileEntityEnergyDirector)
		{
			triggers.add(module_Machines.energyDirectorMinEnergyMetTrigger);
		}
		
		return triggers;
	}*/
	
	
	// - Networking - //
	
	public PacketPayload getPacketPayload()
	{
		PacketPayload payload = new PacketPayload(2, 16, 3);
		
		//array data
		for (int side = 0; side < 4; side++)
			for (int prop = 0; prop < 4; prop++)
			{
				payload.floatPayload[prop + (side * 3)] = this.PowerData[prop][side];
			}
		//route mode boolean
		payload.intPayload[0] = this.routeMode ? 1 : 0;
		//screen text
		for (int index = 0; index < 3; index++)
			payload.stringPayload[index] = this.text[index] != null ? this.text[index] : "";
		//default route side
		payload.intPayload[1] = this.defaultRoute;
			
		return payload;
	}
	
	public void handleUpdatePacket(PacketUpdate packet)
	{
		for (int side = 0; side < 4; side++)
			for (int prop = 0; prop < 4; prop++)
			{
				this.PowerData[prop][side] = packet.payload.floatPayload[prop + (side * 3)];
			}
		this.routeMode = packet.payload.intPayload[0] == 1 ? true : false;
		
		for (int index = 0; index < 3; index++)
			this.text[index] = packet.payload.stringPayload[index];
		
		this.defaultRoute = packet.payload.intPayload[1];
	}
	
	public void handleUpdateMessage(UpdateMessage message)
	{
		setGateValue(message.Side, message.gateValue);
		setMinimumPowerValue(message.Side, message.minValue);
		setPriority(message.Side, (int)message.priority);
		setMode(message.mode);
		
		sendNetworkUpdate();
	}
	
	
	public static class UpdateMessage {
		@TileNetworkData
		public int Side;
		
		@TileNetworkData
		public float minValue;
		
		@TileNetworkData
		public float gateValue;
		
		@TileNetworkData
		public float priority;
		
		@TileNetworkData
		public boolean mode;
	}


	@Override
	public void actionActivated(IAction action) {
		if (action == module_Machines.directorEnableNorth){
			faceSwitch[0] = true;
		} else if (action == module_Machines.directorDisableNorth){
			faceSwitch[0] = false;
		}
	}
}